package org.sgx.jsutil.client;

import com.google.gwt.core.client.JavaScriptObject;

/**
 * I represent a pure JavaScript function object. Use JsUtils.toJsFunction() for transforming a Callback to a JsFunction. Notice that JsFunction inherits from JsObject so it
 * supports all the JavaScript Object methods.
 * 
 * <p>
 * <code>Function</code> objects created with the <code>Function</code> constructor are parsed when the function is created. This is less efficient than declaring a function and
 * calling it within your code, because functions declared with the function statement are parsed with the rest of the code.
 * </p>
 * <p>
 * All arguments passed to the function are treated as the names of the identifiers of the parameters in the function to be created, in the order in which they are passed.
 * </p>
 * <p>
 * <strong>Note: </strong>Functions created with the <code>Function</code> constructor do not create closures to their creation contexts; they always run in the <em>window</em>
 * context (unless the function body starts with a <code>"use strict";</code> statement, in which case the context is <em>undefined</em>).
 * </p>
 * <p>
 * Invoking the <code>Function</code> constructor as a function (without using the <code>new</code> operator) has the same effect as invoking it as a constructor.
 * </p>
 * 
 * <p>
 * Note: In this implementation we only support apply() and do not support call() because for the later a individual java to javascript object type conversion is required. TODO?
 * </p>
 * 
 * @see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function
 * 
 * @author sg
 * 
 */
public class JsFunction extends JsObject {
	protected JsFunction() {
	}

	/**
	 * creates a new Function object calling new Function(body). The resulting function is anonymous.
	 * 
	 * @param body
	 * @return
	 */
	public static native final JsFunction create(String body) /*-{
		return new Function(body);
	}-*/;

	// apply

	/**
	 * applys a function with a given this value and arguments provided individually. In this case this function returns a JavaScriptObject.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final JavaScriptObject applyObject(JavaScriptObject thisArg, JavaScriptObject args) /*-{
		return this.apply(thisArg, args);
	}-*/;

	/**
	 * applys a function with a given this value and arguments provided individually. In this case this function returns a javascript array.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final JsArray applyArray(JavaScriptObject thisArg, JavaScriptObject args) /*-{
		return this.apply(thisArg, args);
	}-*/;

	/**
	 * applys a function with a given this value and arguments provided individually.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final void apply(JavaScriptObject thisArg, JavaScriptObject args) /*-{
		return this.apply(thisArg, args);
	}-*/;

	/**
	 * applys a function with a given this value and arguments provided individually. In this case this function returns a string.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final String applyString(JavaScriptObject thisArg, JavaScriptObject args) /*-{
		return this.apply(thisArg, args);
	}-*/;

	/**
	 * applys a function with a given this value and arguments provided individually. In this case this function returns a number.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final double applyDouble(JavaScriptObject thisArg, JavaScriptObject args) /*-{
		return this.apply(thisArg, args);
	}-*/;

	/**
	 * applys a function with a given this value and arguments provided individually. In this case this function returns a Integer.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final double applyInteger(JavaScriptObject thisArg, JavaScriptObject args) /*-{
		return this.apply(thisArg, args);
	}-*/;

	/**
	 * applys a function with a given this value and arguments provided individually. In this case this function returns a Boolean.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final boolean applyBoolean(JavaScriptObject thisArg, JavaScriptObject args) /*-{
		return this.apply(thisArg, args);
	}-*/;

	/**
	 * applys a function with a given this value and arguments provided individually. In this case this function returns a JavaScriptObject.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final JavaScriptObject applyObject(JavaScriptObject args) /*-{
		return this.apply(args);
	}-*/;

	/**
	 * applys a function with a given this value and arguments provided individually. In this case this function returns a javascript array.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final JsArray applyArray(JavaScriptObject args) /*-{
		return this.apply(null, args);
	}-*/;

	/**
	 * applys a function with a given this value and arguments provided individually.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final void apply(JavaScriptObject args) /*-{
		return this.apply(null, args);
	}-*/;

	/**
	 * applys a function with a given this value and arguments provided individually. In this case this function returns a string.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final String applyString(JavaScriptObject args) /*-{
		return this.apply(null, args);
	}-*/;

	/**
	 * applys a function with a given this value and arguments provided individually. In this case this function returns a number.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final double applyDouble(JavaScriptObject args) /*-{
		return this.apply(null, args);
	}-*/;

	/**
	 * applys a function with a given this value and arguments provided individually. In this case this function returns a Integer.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final int applyInteger(JavaScriptObject args) /*-{
		return this.apply(null, args);
	}-*/;

	/**
	 * applys a function with a given this value and arguments provided individually. In this case this function returns a Boolean.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final boolean applyBoolean(JavaScriptObject args) /*-{
		return this.apply(null, args);
	}-*/;

	/**
	 * The function's name. Example showing what this property means:
	 * 
	 * <pre>
	 * var f1 = function(){}; 
	 * alert(f1.name); // result: "" - the function is anonymous
	 * var f2 = function openTheDoor(){}
	 * alert(f2.name); //result: "openTheDoor"
	 * </pre>
	 * 
	 * @return the function name. In the case of anonymous functions it returns an empty string.
	 */
	public native final String name() /*-{
		return this.name || "";
	}-*/;

	/**
	 * call the function as if were a class constructor, like:
	 * 
	 * <pre>
	 * return new f(args);
	 * </pre>
	 * 
	 * Particularly useful for creating new object instance using a constructor function.
	 */
	public native final void construct(JavaScriptObject args) /*-{
		return new this(args);
	}-*/;

	/**
	 * Creates a new Function Object
	 * 
	 * @param argNames
	 *            Names to be used by the function as formal argument names. Each must be a string that corresponds to a valid JavaScript identifier or a list of such strings
	 *            separated with a comma; for example "x", "theValue", or "a,b".
	 * @param functionBody
	 *            A string containing the JavaScript statements comprising the function definition.
	 */
	public native final void create(String[] argNames, String functionBody) /*-{
		var jargs = @org.sgx.jsutil.client.JsUtil::toJsArrayString([Ljava/lang/Object;)(argNames);
		var args = [];
		return new this(args);
	}-*/;

	/**
	 * Function objects inherit from Function.prototype. Modifications to the Function.prototype object are propagated to all Function instances.
	 * 
	 * @see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/prototype
	 * @return
	 */
	public native final JsObject prototype() /*-{
		return this.prototype;
	}-*/;

	/**
	 * Function objects inherit from Function.prototype. Modifications to the Function.prototype object are propagated to all Function instances.
	 * 
	 * @see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/prototype
	 * @return
	 */
	public native final JsObject FunctionPrototype() /*-{
		return Function.prototype;
	}-*/;

	/**
	 * An array corresponding to the arguments passed to a function. This is deprecated as property of Function, use the arguments object available within the function instead. in
	 * gwtjsutil use JsUtil.arguments()
	 * 
	 * @return
	 */
	public native final <T extends JavaScriptObject> T arguments() /*-{
		return this.arguments;
	}-*/;

	/**
	 * 
	 Specifies the number of arguments expected by the function. Use the length property instead.
	 */
	public native final int arity() /*-{
		return this.arity;
	}-*/;

	/**
	 * Non-standard. Returns the function that invoked the currently executing function.
	 * 
	 * <p>
	 * This property is not part of ECMA-262 Edition 3 standard. It is implemented at least in <a href="/en/SpiderMonkey" title="en/SpiderMonkey">SpiderMonkey</a> (the JavaScript
	 * engine used in Mozilla) (see <a class="external" href="https://bugzilla.mozilla.org/show_bug.cgi?id=65683" title="">bug 65683</a>), V8 (the JavaScript engine used in Chrome)
	 * and JScript.
	 * </p>
	 * <p>
	 * If the function <code>f</code> was invoked by the top level code, the value of <code>f.caller</code> is <code>null</code>, otherwise it's the function that called
	 * <code>f</code>
	 * </p>
	 * <p>
	 * This property replaces deprecated <a href="/en/JavaScript/Reference/Functions_and_function_scope/arguments/caller"
	 * title="En/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments/caller">arguments.caller</a>.
	 * </p>
	 * <h3 id="Notes" name="Notes">Notes</h3>
	 * <p>
	 * Note that in case of recursion, you can't reconstruct the call stack using this property. Consider:
	 * </p>
	 * 
	 * <pre class="eval">
	 * function f(n) { g(n-1) }
	 * function g(n) { if(n&gt;0) f(n); else stop() }
	 * f(2)
	 * </pre>
	 * <p>
	 * At the moment <code>stop()</code> is called the call stack will be:
	 * </p>
	 * 
	 * <pre class="eval">
	 * f(2) -&gt; g(1) -&gt; f(1) -&gt; g(0) -&gt; stop()
	 * </pre>
	 * <p>
	 * The following is true:
	 * </p>
	 * 
	 * <pre class="eval">
	 * stop.caller === g &amp;&amp; f.caller === g &amp;&amp; g.caller === f
	 * </pre>
	 * <p>
	 * so if you tried to get the stack trace in the <code>stop()</code> function like this:
	 * </p>
	 * 
	 * <pre class="eval">
	 * var f = stop;
	 * var stack = &quot;Stack trace:&quot;;
	 * while (f) {
	 * 	stack += &quot;\n&quot; + f.name;
	 * 	f = f.caller;
	 * }
	 * </pre>
	 * <p>
	 * the loop would never stop.
	 * </p>
	 * <p>
	 * The special property <code>__caller__</code>, which returned the activation object of the caller thus allowing to reconstruct the stack, was removed for security reasons.
	 * </p>
	 * <h3 id="Examples" name="Examples">Examples</h3> <h4 id="Example:_Checking_the_value_of_a_function.27s_caller_property" name="Example:_Checking_the_value_of_a_function.27s_caller_property">
	 * Example: Checking the value of a function's <code>caller</code> property</h4>
	 * <p>
	 * The following code checks the value a function's <code>caller</code> property.
	 * </p>
	 * 
	 * <pre class="eval">
	 * function myFunc() {
	 * 	if (myFunc.caller == null) {
	 * 		return (&quot;The function was called from the top!&quot;);
	 * 	} else
	 * 		return (&quot;This function's caller was &quot; + myFunc.caller);
	 * }
	 * </pre>
	 * <p>
	 * &nbsp;
	 * </p>
	 * <h3 id="Browser_Support">Browser Support</h3>
	 * <p>
	 * Function.caller is currently supported by all major browsers:&nbsp;Firefox, Safari, Chrome, Opera and IE. &nbsp;<a class="external"
	 * href="http://kangax.github.com/es5-compat-table/non-standard/">ECMAScript extensions compatibility table</a>.
	 * </p>
	 * 
	 * @return the function that invoked the currently executing function.
	 */
	public native final JsFunction caller() /*-{
		return this.caller;
	}-*/;

	public native final static JsFunction create(Callback callback)/*-{
		return @org.sgx.jsutil.client.JsUtil::toJsFunction(Lorg/sgx/jsutil/client/Callback;)(callback); 
	}-*/;


	// /**
	// * Returns a reference to the Function function that created the instance's prototype. Note that the value of this property is a reference to the function itself, not a
	// string
	// * containing the function's name.
	// *
	// * @return
	// */
	// public native final JsFunction constructor() /*-{
	// return this.constructor;
	// }-*/;

	// /**
	// * Calls a function with a given this value and arguments provided individually. In this case this function returns a JavaScriptObject.
	// *
	// * @param thisArg
	// * @param args
	// * @return
	// */
	// public native final JavaScriptObject callObject(JavaScriptObject thisArg, JavaScriptObject args) /*-{
	// return this.call(thisArg, args);
	// }-*/;
	//
	// /**
	// * Calls a function with a given this value and arguments provided individually. In this case this function returns a javascript array.
	// *
	// * @param thisArg
	// * @param args
	// * @return
	// */
	// public native final <T> JsArray<T> callArray(JavaScriptObject thisArg, JavaScriptObject args) /*-{
	// return this.call(thisArg, args);
	// }-*/;
	//
	// /**
	// * Calls a function with a given this value and arguments provided individually.
	// *
	// * @param thisArg
	// * @param args
	// * @return
	// */
	// public native final void call(JavaScriptObject thisArg, JavaScriptObject args) /*-{
	// return this.call(thisArg, args);
	// }-*/;
	//
	// /**
	// * Calls a function with a given this value and arguments provided individually. In this case this function returns a string.
	// *
	// * @param thisArg
	// * @param args
	// * @return
	// */
	// public native final String callString(JavaScriptObject thisArg, JavaScriptObject args) /*-{
	// return this.call(thisArg, args);
	// }-*/;
	//
	// /**
	// * Calls a function with a given this value and arguments provided individually. In this case this function returns a number.
	// *
	// * @param thisArg
	// * @param args
	// * @return
	// */
	// public native final double callDouble(JavaScriptObject thisArg, JavaScriptObject args) /*-{
	// return this.call(thisArg, args);
	// }-*/;
	//
	// /**
	// * Calls a function with a given this value and arguments provided individually. In this case this function returns a Integer.
	// *
	// * @param thisArg
	// * @param args
	// * @return
	// */
	// public native final double callInteger(JavaScriptObject thisArg, JavaScriptObject args) /*-{
	// return this.call(thisArg, args);
	// }-*/;
	//
	// /**
	// * Calls a function with a given this value and arguments provided individually. In this case this function returns a Boolean.
	// *
	// * @param thisArg
	// * @param args
	// * @return
	// */
	// public native final boolean callBoolean(JavaScriptObject thisArg, JavaScriptObject args) /*-{
	// return this.call(thisArg, args);
	// }-*/;
}
